package top.xzxsrq.dynamicObject;

import javassist.*;
import javassist.bytecode.*;
import javassist.bytecode.annotation.*;
import org.apache.commons.collections4.CollectionUtils;
import top.xzxsrq.common.utils.ObjectUtilsZX;
import top.xzxsrq.common.utils.StringUtilsZX;
import top.xzxsrq.dynamicObject.MyMemberValueVisitor.ReplaceMemberValueVisitorZX;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: MyUtils
 * @create: 2021-08-25 20:29
 **/
public class DynamicUtilsZX {
    public static CtField makeFieldAndGetAndSet(AllUseCoreClassZX emptyClass, String fieldName) throws CannotCompileException, NotFoundException {
        return makeFieldAndGetAndSet(emptyClass, fieldName, String.class);
    }

    public static CtField makeFieldAndGetAndSet(AllUseCoreClassZX emptyClass, String fieldName, Class<?> type) throws CannotCompileException, NotFoundException {
        CtField field = new CtField(emptyClass.getPool().get(type.getName()), fieldName, emptyClass.getObject());
        // 访问级别是 private
        field.setModifiers(Modifier.PRIVATE);
        // 加入字段
        emptyClass.getObject().addField(field);
        makeGetAndSet(emptyClass, field);
        return field;
    }

    public static CtField makeFieldAndGetAndSet(AllUseCoreClassZX emptyClass, String fieldName, String typeName) throws CannotCompileException, NotFoundException {
        CtField field = new CtField(emptyClass.getPool().get(typeName), fieldName, emptyClass.getObject());
        // 访问级别是 private
        field.setModifiers(Modifier.PRIVATE);
        // 加入字段
        emptyClass.getObject().addField(field);
        makeGetAndSet(emptyClass, field);
        return field;
    }

    public static void makeGetAndSet(AllUseCoreClassZX emptyClass, CtField field) throws CannotCompileException {
        String fieldName = field.getName();
        emptyClass.getObject().addMethod(CtNewMethod.setter("set" + StringUtilsZX.captureName(fieldName), field));
        emptyClass.getObject().addMethod(CtNewMethod.getter("get" + StringUtilsZX.captureName(fieldName), field));
    }

    public static void makeToString(AllUseCoreClassZX emptyClass, List<String> fieldsList, boolean callSuper) throws CannotCompileException, NotFoundException {
        List<String> collect = fieldsList.stream().distinct().collect(Collectors.toList());
        removeMethod(emptyClass, "toString", null);
        // 创建toString方法
        CtClass stringClass = emptyClass.getPool().get(String.class.getCanonicalName());
        CtMethod toString = new CtMethod(stringClass, "toString", null, emptyClass.getObject());
        Annotation toStringOverride = new Annotation(Override.class.getCanonicalName(), emptyClass.getConstPool());
        AnnotationsAttribute toStringAttribute = new AnnotationsAttribute(emptyClass.getConstPool(), AnnotationsAttribute.visibleTag);
        toStringAttribute.addAnnotation(toStringOverride);
        toString.setModifiers(Modifier.PUBLIC);
        toString.getMethodInfo().addAttribute(toStringAttribute);
        emptyClass.getObject().addMethod(toString);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("{");
        stringBuilder.append("return \"").append(emptyClass.getObject().getName()).append(":\"");
        int i = 0;
        for (String field : collect) {
            if (i++ != 0) {
                stringBuilder.append("+\",\"+");
            } else {
                CtClass superclass = emptyClass.getObject().getSuperclass();
                if (callSuper && ObjectUtilsZX.isNotEmpty(superclass) && !superclass.getName().equals(Object.class.getName())) {
                    stringBuilder.append("+\"[super=[\"")
                            .append("+super.toString()+")
                            .append("\"],\"+");
                } else {
                    stringBuilder.append("\"[\"+");
                }
            }
            stringBuilder.append("\"").append(field).append("=\"");
            stringBuilder.append("+$0.").append(field);
        }
        if (i == 0) {
            stringBuilder.append("\"\"");
        } else {
            stringBuilder.append("+\"]\"");
        }
        stringBuilder.append(";");
        stringBuilder.append("}");
        toString.setBody(stringBuilder.toString());
    }

    /**
     * 首先要移除原来的方法
     *
     * @param emptyClass
     * @param fieldsList
     * @throws CannotCompileException
     * @throws NotFoundException
     */
    public static void makeToString(AllUseCoreClassZX emptyClass, List<String> fieldsList) throws CannotCompileException, NotFoundException {
        makeToString(emptyClass, fieldsList, false);
    }

    /**
     * 多个注解必须是一次添加
     * 因为AnnotationsAttribute必须是一个
     *
     * @param method
     * @param annotation
     */
    public static void methodAddAnnotation(CtMethod method, Class<?> annotation) {
        Annotation toStringOverride = new Annotation(annotation.getCanonicalName(), method.getMethodInfo().getConstPool());
        AnnotationsAttribute toStringAttribute = new AnnotationsAttribute(method.getMethodInfo().getConstPool(), AnnotationsAttribute.visibleTag);
        toStringAttribute.addAnnotation(toStringOverride);
    }

    public static void annotationAddValue(Annotation annotation, AllUseCoreClassZX emptyClass, String name, Integer value) {
        annotationAddValue(annotation, emptyClass.getConstPool(), name, value);
    }

    public static void annotationAddValue(Annotation annotation, ConstPool constPool, String name, int value) {
        IntegerMemberValue integerMemberValue = new IntegerMemberValue(constPool);
        integerMemberValue.setValue(value);
        annotation.addMemberValue(name, integerMemberValue);
    }

    public static void annotationAddValue(Annotation annotation, AllUseCoreClassZX emptyClass, String name, Integer[] value) {
        annotationAddValue(annotation, emptyClass.getConstPool(), name, value);
    }

    public static void annotationAddValue(Annotation annotation, ConstPool constPool, String name, Integer[] value) {
        ArrayMemberValue arrayMemberValue = new ArrayMemberValue(constPool);
        arrayMemberValue.setValue(Arrays.stream(value).map(i -> {
            IntegerMemberValue integerMemberValue = new IntegerMemberValue(constPool);
            integerMemberValue.setValue(i);
            return integerMemberValue;
        }).toArray(IntegerMemberValue[]::new));
        annotation.addMemberValue(name, arrayMemberValue);
    }

    public static void annotationAddValue(Annotation annotation, AllUseCoreClassZX emptyClass, String name, String value) {
        annotationAddValue(annotation, emptyClass.getConstPool(), name, value);
    }

    public static void annotationAddValue(Annotation annotation, ConstPool constPool, String name, String value) {
        StringMemberValue stringMemberValue = new StringMemberValue(constPool);
        stringMemberValue.setValue(value);
        annotation.addMemberValue(name, stringMemberValue);
    }

    public static void annotationAddValue(Annotation annotation, AllUseCoreClassZX emptyClass, String name, String[] value) {
        annotationAddValue(annotation, emptyClass.getConstPool(), name, value);
    }

    public static void annotationAddValue(Annotation annotation, ConstPool constPool, String name, String[] value) {
        ArrayMemberValue arrayMemberValue = new ArrayMemberValue(constPool);
        arrayMemberValue.setValue(Arrays.stream(value).map(i -> {
            StringMemberValue stringMemberValue = new StringMemberValue(constPool);
            stringMemberValue.setValue(i);
            return stringMemberValue;
        }).toArray(StringMemberValue[]::new));
        annotation.addMemberValue(name, arrayMemberValue);
    }

    public static void annotationAddValue(Annotation annotation, AllUseCoreClassZX emptyClass, String name, Enum<?> value) {
        annotationAddValue(annotation, emptyClass.getConstPool(), name, value);
    }

    public static void annotationAddValue(Annotation annotation, ConstPool constPool, String name, Enum<?> value) {
        EnumMemberValue enumMemberValue = new EnumMemberValue(constPool);
        enumMemberValue.setType(value.getClass().getTypeName());
        enumMemberValue.setValue(value.name());
        annotation.addMemberValue(name, enumMemberValue);
    }

    public static void annotationAddValue(Annotation annotation, AllUseCoreClassZX emptyClass, String name, Enum<?>[] value) {
        annotationAddValue(annotation, emptyClass.getConstPool(), name, value);
    }

    public static void annotationAddValue(Annotation annotation, ConstPool constPool, String name, Enum<?>[] value) {
        ArrayMemberValue arrayMemberValue = new ArrayMemberValue(constPool);
        EnumMemberValue[] enumMemberValues = Arrays.stream(value)
                .map(i -> {
                    EnumMemberValue enumMemberValue = new EnumMemberValue(constPool);
                    enumMemberValue.setType(i.getClass().getTypeName());
                    enumMemberValue.setValue(i.name());
                    return enumMemberValue;
                })
                .toArray(EnumMemberValue[]::new);
        arrayMemberValue.setValue(enumMemberValues);
        annotation.addMemberValue(name, arrayMemberValue);
    }

    public static boolean hasAnInterface(Class<?> clazz, Class<?> interfaceClass) {
        for (Class<?> anInterface : clazz.getInterfaces()) {
            if (anInterface.getTypeName().equals(interfaceClass.getTypeName())) {
                return true;
            }
        }
        return false;
    }

    private DynamicUtilsZX() {
    }

    /**
     * 多个注解必须是一次添加
     * 调用重载
     * 要先生成注解然后给注解加值,最后加到字段等上面
     *
     * @param ctField
     * @param annotation
     */
    public static void fieldAddAnnotation(CtField ctField, Annotation annotation) {
        AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(ctField.getFieldInfo().getConstPool(), AnnotationsAttribute.visibleTag);
        annotationsAttribute.addAnnotation(annotation);
        fieldAddAnnotationsAttribute(ctField, annotationsAttribute);
    }

    /**
     * 多个注解必须是一次添加
     * 因为AnnotationsAttribute必须是一个
     *
     * @param ctField
     * @param annotation
     */
    public static void fieldAddAnnotation(CtField ctField, List<Annotation> annotation) {
        AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(ctField.getFieldInfo().getConstPool(), AnnotationsAttribute.visibleTag);
        annotation.forEach(annotationsAttribute::addAnnotation);
        fieldAddAnnotationsAttribute(ctField, annotationsAttribute);
    }

    public static void fieldAddAnnotationsAttribute(CtField ctField, AnnotationsAttribute annotationsAttribute) {
        FieldInfo fieldInfo = ctField.getFieldInfo();
        if (ObjectUtilsZX.isNotEmpty(fieldInfo)) {
            fieldInfo.addAttribute(annotationsAttribute);
        }
    }

    /**
     * 要先生成注解然后给注解加值,最后加到字段等上面
     *
     * @param annotation
     * @param emptyClass
     * @return
     */
    public static Annotation makeAnnotation(Class<?> annotation, AllUseCoreClassZX emptyClass) {
        return new Annotation(annotation.getCanonicalName(), emptyClass.getConstPool());
    }

    public static boolean removeField(AllUseCoreClassZX emptyClass, String name) {
        try {
            CtField field = emptyClass.getObject().getField(name);
            if (field != null) {
                emptyClass.getObject().removeField(field);
            }
            return true;
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static boolean removeMethod(AllUseCoreClassZX emptyClass, String name, CtClass[] params) {
        try {
            CtMethod method = emptyClass.getObject().getDeclaredMethod(name, params);
            emptyClass.getObject().removeMethod(method);
            return true;
        } catch (NotFoundException ignored) {
        }
        return false;
    }

    /**
     * 注解跟新值 可以写很多的重载
     * FieldInfo fieldInfo = field.getFieldInfo(); // 字段或者方法等 拿到info
     * AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
     *
     * @param field
     * @param attribute
     * @param annotationName
     * @param name
     * @param value
     */
    public static void annotationUpdateValue(CtField field, AnnotationsAttribute attribute, Class<?> annotationName, String name, String[] value) {
        Annotation annotation = attribute.getAnnotation(annotationName.getName());
        ArrayMemberValue memberValue = (ArrayMemberValue) annotation.getMemberValue(name);
        if (ObjectUtilsZX.isEmpty(memberValue)) {
            annotationAddValue(annotation, field.getFieldInfo().getConstPool(), name, value);
        } else {
            List<StringMemberValue> newValue = new LinkedList<>();
            for (String s : value) {
                StringMemberValue item = new StringMemberValue(field.getFieldInfo().getConstPool());
                item.setValue(s);
                newValue.add(item);
            }
            memberValue.setValue(newValue.toArray(new StringMemberValue[0]));
            annotation.addMemberValue(name, memberValue);
        }
        attribute.removeAnnotation(annotationName.getName());
        attribute.addAnnotation(annotation);
        field.getFieldInfo().addAttribute(attribute);
    }

    public static void annotationUpdateValue(CtField field, AnnotationsAttribute attribute, Class<?> annotationName, String name, int value) {
        Annotation annotation = attribute.getAnnotation(annotationName.getName());
        IntegerMemberValue memberValue = (IntegerMemberValue) annotation.getMemberValue(name);
        if (ObjectUtilsZX.isEmpty(memberValue)) {
            annotationAddValue(annotation, field.getFieldInfo().getConstPool(), name, value);
        } else {
            memberValue.setValue(value);
            annotation.addMemberValue(name, memberValue);
        }
        attribute.removeAnnotation(annotationName.getName());
        attribute.addAnnotation(annotation);
        field.getFieldInfo().addAttribute(attribute);
    }

    /**
     * 货值注解的值
     * FieldInfo fieldInfo = field.getFieldInfo(); // 字段或者方法等 拿到info
     * AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
     *
     * @param attribute
     * @param annotationName
     * @param name
     * @return
     */
    public static MemberValue getAnnotationValue(AnnotationsAttribute attribute, Class<?> annotationName, String name) {
        Annotation annotation = attribute.getAnnotation(annotationName.getName());
        return annotation.getMemberValue(name);
    }

    /**
     * 包装
     * <pre>
     * FieldInfo fieldInfo = field.getFieldInfo(); // 字段或者方法等 拿到info
     * AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
     * </pre>
     *
     * @param field
     * @return
     */
    public static AnnotationsAttribute fieldGetAnnotationsAttribute(CtField field) {
        FieldInfo fieldInfo = field.getFieldInfo(); // 字段或者方法等 拿到info
        return (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
    }

    /**
     * 包装
     * <pre>
     * MethodInfo methodInfo = method.getMethodInfo(); // 字段或者方法等 拿到info
     * AnnotationsAttribute attribute = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
     * </pre>
     *
     * @param method
     * @return
     */
    public static AnnotationsAttribute methodGetAnnotationsAttribute(CtMethod method) {
        MethodInfo methodInfo = method.getMethodInfo(); // 字段或者方法等 拿到info
        return (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
    }

    public static AnnotationsAttribute classGetAnnotationsAttribute(AllUseCoreClassZX clazz) {
        ClassFile classFile = clazz.getClassFile();
        AttributeInfo attribute = classFile.getAttribute(AnnotationsAttribute.visibleTag);
        return (AnnotationsAttribute) attribute;
    }

    /**
     * 实现占位符替换,替换的前后字段顺序不能改变
     * <pre>
     * 如果这个是字段  repalce 的key为查找要替换的位置 , value为替换的值
     * 如果是注解 替换文字的内容
     * </pre>
     *
     * @param clazz    替换的class
     * @param repalce  替换的内容
     *                 <pre></pre>
     * @param plaBegin 占位符前
     * @param plaEnd   占位符结束
     * @return
     * @throws Exception
     */
    public static AllUseCoreClassZX placeholderReplacement(AllUseCoreClassZX clazz, Map<String, String> repalce, String plaBegin, String plaEnd) throws Exception {
        CtClass object = clazz.getObject();
        // 字段上的处理
        CtField[] declaredFields = object.getDeclaredFields();
        for (CtField declaredField : declaredFields) {
            placeholderReplacement(declaredField, repalce, plaBegin, plaEnd); // 字段匹配
            AnnotationsAttribute annotationsAttribute = fieldGetAnnotationsAttribute(declaredField); // 获取字段上注解属性
            if (ObjectUtilsZX.isEmpty(annotationsAttribute)) {
                continue;
            }
            Annotation[] annotations = annotationsAttribute.getAnnotations(); // 获取所有注解
            for (Annotation annotation : annotations) {
                placeholderReplacement(clazz, annotation, repalce, plaBegin, plaEnd); // 字段上的注解进行匹配
                annotationsAttribute.removeAnnotation(annotation.getTypeName()); // 移除原来的注解
                annotationsAttribute.addAnnotation(annotation); // 添加修改后的注解
            }
            declaredField.getFieldInfo().addAttribute(annotationsAttribute); // 把注解属性添加进来
        }
        // class上的处理
        AnnotationsAttribute attribute = classGetAnnotationsAttribute(clazz);
        if (ObjectUtilsZX.isNotEmpty(attribute)) {
            Annotation[] annotations = attribute.getAnnotations();
            for (Annotation annotation : annotations) {
                placeholderReplacement(clazz, annotation, repalce, plaBegin, plaEnd);
            }
        }
        return clazz;
    }


    /**
     * 注解的名字是别人定义好的无法修改
     *
     * @param emptyClass
     * @param annotation
     * @param repalce
     * @param plaBegin
     * @param plaEnd
     */
    public static void placeholderReplacement(AllUseCoreClassZX emptyClass, Annotation annotation, Map<String, String> repalce, String plaBegin, String plaEnd) {
        Set<String> memberNames = annotation.getMemberNames();
        for (String memberName : memberNames) {
            MemberValue memberValue = annotation.getMemberValue(memberName);
            memberValue.accept(new ReplaceMemberValueVisitorZX(emptyClass, memberName, repalce, plaBegin, plaEnd));
            annotation.addMemberValue(memberName, memberValue);
        }
    }

    public static void placeholderReplacement(CtField declaredField, Map<String, String> repalce, String plaBegin, String plaEnd) {
        String name = declaredField.getName();
        name = ReplaceMemberValueVisitorZX.repalceMap(repalce, plaBegin, plaEnd, name);
        declaredField.setName(name);
    }

    /**
     * 获取所有的定义字段描述
     *
     * @param coreClass
     * @return
     */
    public static List<CtField> getCtFieldsAndSup(AllUseCoreClassZX coreClass) {
        CtClass object = coreClass.getObject();
        if (object.isInterface()) {
            throw new RuntimeException("描述对象不能是接口");
        }
        return getCtFieldsAndSup(object);
    }

    /**
     * 获取所有的定义字段描述
     *
     * @param object
     * @return
     */
    private static List<CtField> getCtFieldsAndSup(CtClass object) {
        CtField[] declaredFields = object.getDeclaredFields();
        List<CtField> ctFields = Arrays.asList(declaredFields);
        try {
            CtClass superclass = object.getSuperclass();
            if (superclass == null) {
                return ctFields;
            }
            ctFields.addAll(getCtFieldsAndSup(superclass));
        } catch (NotFoundException ignore) {
        }
        return ctFields;
    }

    private static void mergeObject(AllUseCoreClassZX emptyClass, List<AllUseCoreClassZX> source) throws Exception {
        for (AllUseCoreClassZX emptyClass1 : source) {
            removeField(emptyClass1, "serialVersionUID");
            List<CtField> ctFieldsAndSup = getCtFieldsAndSup(emptyClass1);
            for (CtField ctField : ctFieldsAndSup) {
                CtField ctField1 = makeFieldAndGetAndSet(emptyClass, ctField.getName(), ctField.getType().getName());
                AnnotationsAttribute annotationsAttribute = fieldGetAnnotationsAttribute(ctField);
                fieldAddAnnotationsAttribute(ctField1, annotationsAttribute);
            }
        }
    }

    /**
     * 把source的key匹配到target的字段名字然后替换成source的value的所有key
     *
     * @param target
     * @param source
     * @return
     * @throws Exception
     */
    public static AllUseCoreClassZX mergeObject(AllUseCoreClassZX target, Map<String, List<AllUseCoreClassZX>> source) throws Exception {
        AllUseCoreClassZX emptyClass = AllUseCoreClassZX.getEmptyClass();
        // 把对象上target对象的注解拿过来
        AnnotationsAttribute annotationsAttribute1 = classGetAnnotationsAttribute(target);
        if (ObjectUtilsZX.isNotEmpty(annotationsAttribute1)) {
            emptyClass.getClassFile().addAttribute(annotationsAttribute1);
        }
        // 合并字段
        removeField(target, "serialVersionUID");
        List<CtField> ctFieldsAndSup = getCtFieldsAndSup(target);
        for (CtField declaredField : ctFieldsAndSup) {
            String name = declaredField.getName();
            List<AllUseCoreClassZX> classes = source.get(name);
            if (CollectionUtils.isNotEmpty(classes)) {
                mergeObject(emptyClass, classes);
            } else {
                CtField ctField = makeFieldAndGetAndSet(emptyClass, name, declaredField.getType().getName());
                AnnotationsAttribute annotationsAttribute = fieldGetAnnotationsAttribute(declaredField);
                fieldAddAnnotationsAttribute(ctField, annotationsAttribute);
            }
        }
        List<AllUseCoreClassZX> classes = source.get("merge_zhi_xin_1998_*2");
        if (CollectionUtils.isNotEmpty(classes)) {
            mergeObject(emptyClass, classes);
        }
        return emptyClass;
    }

    /**
     * 直接把source的字段往target后面添加
     *
     * @param target
     * @param source
     * @return
     * @throws Exception
     */
    public static AllUseCoreClassZX mergeObject(AllUseCoreClassZX target, AllUseCoreClassZX source) throws Exception {
        Map<String, List<AllUseCoreClassZX>> map = new HashMap<>();
        List<AllUseCoreClassZX> list = new LinkedList<>();
        list.add(source);
        map.put("merge_zhi_xin_1998_*2", list);
        return mergeObject(target, map);
    }
}
